const toggleRowMenu = (id) => {
  openMenuId.value = openMenuId.value === id ? null : id
}
const closeRowMenu = () => { openMenuId.value = null }
// close menus on outside click
if (typeof window !== 'undefined') {
  window.addEventListener('click', () => { openMenuId.value = null })
}
<template>
  <div>
    <div class="p-6 min-h-screen">
      <div class="flex justify-between items-center mb-4">
        <h1 class="text-2xl font-bold text-gray-800">问题列表</h1>
        <button class="bg-blue-600 text-white px-4 py-2 rounded" @click="openNew">新建问题</button>
      </div>

      <div class="flex gap-4">
        <!-- Sidebar: Folder Tree -->
        <aside class="w-80 flex-none bg-white rounded p-4 shadow overflow-auto h-[calc(100vh-48px)]">

          <FolderTree @select="onFolderSelect" @drop="onFolderDrop" />
          <div class="mt-4 pt-4 border-t">
             <div class="cursor-pointer px-2 py-1 rounded hover:bg-gray-100 flex items-center gap-2" 
                  :class="{ 'bg-blue-50 text-blue-600': selectedFolderId === 'trash' }"
                  @click="onFolderSelect('trash')">
               <span class="text-lg">🗑️</span>
               <span>草稿箱</span>
             </div>
             <button v-if="selectedFolderId === 'trash'" class="mt-2 w-full border rounded px-3 py-2 text-red-600 border-red-200 hover:bg-red-50 text-sm" @click="emptyTrash">清空草稿箱</button>
          </div>
        </aside>

        <!-- Main -->
        <div class="flex-1">
          <!-- Breadcrumb -->
          <div class="text-sm text-gray-600 mb-2" v-if="breadcrumb.length">
            <span class="opacity-70">当前位置：</span>
            <span v-for="(b, idx) in breadcrumb" :key="b.id">
              <button class="hover:underline" @click="onFolderSelect(b.id)">{{ b.name }}</button>
              <span v-if="idx < breadcrumb.length - 1"> / </span>
            </span>
          </div>
          <div class="bg-white rounded p-4 shadow mb-4 grid md:grid-cols-6 gap-3">
            <select v-model="filters.status" class="border rounded px-3 py-2">
              <option value="">全部状态</option>
              <option value="todo">待处理</option>
              <option value="doing">处理中</option>
              <option value="verify">待验证</option>
              <option value="done">已解决</option>
            </select>
            <select v-model="filters.priority" class="border rounded px-3 py-2">
              <option value="">全部优先级</option>
              <option value="P0">紧急</option>
              <option value="P1">高</option>
              <option value="P2">中</option>
              <option value="P3">低</option>
            </select>
            <input v-model="keyword" class="border rounded px-3 py-2" placeholder="搜索标题..." />
            <label class="flex items-center gap-2 text-sm text-gray-600">
              <input type="checkbox" v-model="includeDesc" /> 含子目录
            </label>
            <button class="border rounded px-3 py-2" @click="load">筛选</button>
            <button class="border rounded px-3 py-2" @click="showAll">全部问题</button>
          </div>

          <div class="bg-white rounded shadow overflow-x-auto">
            <table class="min-w-full table-fixed">
              <thead>
                <tr class="border-b bg-gray-50 text-left text-sm text-gray-600">
                  <th class="p-3">标题</th>
                  <th class="p-3 w-28 text-center whitespace-nowrap">状态</th>
                  <th class="p-3 w-24 text-center whitespace-nowrap">优先级</th>
                  <th class="p-3 w-48 text-center whitespace-nowrap">截止时间</th>
                  <th class="p-3 w-16 text-center">操作</th>
                </tr>
              </thead>
              <tbody>
                <tr v-for="i in filtered" :key="i.id" class="border-b hover:bg-gray-50 cursor-pointer"
                    :class="{ 'bg-red-50': i.priority === 'P0', 'bg-yellow-50': i.priority === 'P1' }"
                    draggable="true"
                    @dragstart="onDragStart(i, $event)"
                    @dragover.prevent
                    @drop="onRowDrop(i)"
                    @click="openDetail(i)">
                  <td class="p-3">
                    <div class="font-medium text-gray-800">{{ i.title }}</div>
                    <div class="text-xs text-gray-500 truncate max-w-[480px]">{{ stripHtml(i.description) }}</div>
                  </td>
                  <td class="p-3 text-sm text-center whitespace-nowrap">{{ statusText(i.status) }}</td>
                  <td class="p-3 text-center whitespace-nowrap"><span :class="badgeClass(i.priority) + ' inline-block min-w-[1.75rem] text-center'">{{ priorityText(i.priority) }}</span></td>
                  <td class="p-3 text-sm text-gray-600 text-center whitespace-nowrap">{{ i.deadline ? new Date(i.deadline).toLocaleString() : '-' }}</td>
                  <td class="p-3 w-16 text-sm relative text-center" @click.stop>
                    <button class="p-1 rounded hover:bg-gray-100" @click="toggleRowMenu(i.id)" title="更多">
                      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-4 h-4 text-gray-600">
                        <path d="M6 10a2 2 0 11-4 0 2 2 0 014 0zm6 0a2 2 0 11-4 0 2 2 0 014 0zm6 0a2 2 0 11-4 0 2 2 0 014 0z"/>
                      </svg>
                    </button>
                    <div v-if="openMenuId===i.id" class="absolute z-10 mt-1 right-2 w-28 bg-white border rounded shadow">
                      <template v-if="selectedFolderId === 'trash'">
                        <button class="w-full text-left px-3 py-2 hover:bg-gray-100" @click="restore(i.id); closeRowMenu()">还原</button>
                        <button class="w-full text-left px-3 py-2 text-red-600 hover:bg-gray-100" @click="hardDelete(i.id); closeRowMenu()">彻底删除</button>
                      </template>
                      <template v-else>
                        <button class="w-full text-left px-3 py-2 hover:bg-gray-100" @click="openMove(i.id); closeRowMenu()">移动</button>
                        <button class="w-full text-left px-3 py-2 text-red-600 hover:bg-gray-100" @click="remove(i.id); closeRowMenu()">删除</button>
                      </template>
                    </div>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>

          <!-- Right Drawer: Issue Detail -->
          <Transition name="fade" appear>
            <div v-if="showDetail" class="fixed inset-0 z-30">
              <div class="absolute inset-0 bg-black/40" @click="closeDetail"></div>
              <Transition enter-active-class="transform transition-transform duration-300" enter-from-class="translate-x-full" enter-to-class="translate-x-0" leave-active-class="transform transition-transform duration-300" leave-from-class="translate-x-0" leave-to-class="translate-x-full">
                <div class="absolute right-0 top-0 bottom-0 w-3/4 bg-white shadow-xl overflow-auto">
                  <button class="absolute top-3 right-3 p-1 rounded hover:bg-gray-100" @click="closeDetail" title="关闭">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5 text-gray-700"><path d="M18 6L6 18M6 6l12 12"/></svg>
                  </button>
                  <IssueDetail v-if="detailId" :issueId="detailId" :embedded="true" ref="detailRef" />
                </div>
              </Transition>
            </div>
          </Transition>
        </div>
      </div>

      <!-- Dialog -->
      <div v-if="show" class="fixed inset-0 bg-black/50 flex items-center justify-center p-4">
        <div class="bg-white rounded-lg shadow-2xl ring-1 ring-black/5 w-full max-w-4xl p-6">
          <h2 class="text-lg font-semibold mb-4">新建问题</h2>
          <div class="space-y-3">
            <input v-model="form.title" class="border rounded px-3 py-2 w-full" placeholder="标题*" />
            <div>
              <RichTextEditor v-model="form.description" :height="420" @ready="editorReady = true" />
            </div>
            <div class="grid grid-cols-3 gap-3">
              <select v-model="form.status" class="border rounded px-3 py-2">
                <option value="todo">待处理</option>
                <option value="doing">处理中</option>
                <option value="verify">待验证</option>
                <option value="done">已解决</option>
              </select>
              <select v-model="form.priority" class="border rounded px-3 py-2">
                <option value="P0">紧急</option>
                <option value="P1">高</option>
                <option value="P2" selected>中</option>
                <option value="P3">低</option>
              </select>
              <input v-model="form.deadline" type="datetime-local" class="border rounded px-3 py-2" />
            </div>
            <div class="text-sm">
              <label class="block mb-1 text-gray-600">选择目录</label>
              <select v-model="formFolderId" class="border rounded px-3 py-2 w-full">
                <option v-for="f in folderOptions" :key="f.id" :value="f.id">{{ f.label }}</option>
              </select>
            </div>
          </div>
          <div class="mt-6 flex justify-end gap-3">
            <button class="px-4 py-2 border rounded" @click="show=false">取消</button>
            <button class="px-4 py-2 bg-blue-600 text-white rounded" @click="submit">保存</button>
          </div>
        </div>
      </div>

      <!-- Move Dialog -->
      <div v-if="showMove" class="fixed inset-0 bg-black/50 flex items-center justify-center p-4">
        <div class="bg-white rounded-lg shadow-2xl ring-1 ring-black/5 w-full max-w-sm p-6">
          <h2 class="text-lg font-semibold mb-4">移动到目录</h2>
          <div class="space-y-3">
            <select v-model="moveFolderId" class="border rounded px-3 py-2 w-full">
              <option :value="null">（无）</option>
              <option v-for="f in folderOptions" :key="f.id" :value="f.id">{{ f.label }}</option>
            </select>
          </div>
          <div class="mt-6 flex justify-end gap-3">
            <button class="px-4 py-2 border rounded" @click="showMove=false">取消</button>
            <button class="px-4 py-2 bg-blue-600 text-white rounded" @click="confirmMove">确定</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, watch, onBeforeUnmount } from 'vue'
import api from '../api'
import FolderTree from '../components/FolderTree.vue'
import IssueDetail from './IssueDetail.vue'
import { useFolderStore } from '../stores/folders'
import RichTextEditor from '../components/RichTextEditor.vue'

const list = ref([])
const keyword = ref('')
const filters = ref({ status: '', priority: '' })
const includeDesc = ref(false)
const selectedFolderId = ref(null)
const folderStore = useFolderStore()
const putInCurrentFolder = ref(true)
const formFolderId = ref(null)
const draggingIssueId = ref(null)

const currentFolderName = computed(() => {
  const id = selectedFolderId.value
  if (!id) return ''
  const f = folderStore.flat.find(x => x.id === id)
  return f?.name || ''
})
const breadcrumb = computed(() => {
  const id = selectedFolderId.value
  if (!id) return []
  const map = new Map(folderStore.flat.map(f => [f.id, f]))
  const path = []
  let cur = map.get(id)
  while (cur) { path.unshift(cur); cur = cur.parent_id ? map.get(cur.parent_id) : null }
  return path
})
const show = ref(false)
const form = ref({ title: '', description: '', status: 'todo', priority: 'P2', deadline: '' })
// Editor state (rich text only)
const editorReady = ref(false)
const openMenuId = ref(null)
const toggleRowMenu = (id) => { openMenuId.value = openMenuId.value === id ? null : id }
const closeRowMenu = () => { openMenuId.value = null }
if (typeof window !== 'undefined') {
  window.addEventListener('click', () => { openMenuId.value = null })
}

const badgeClass = (p) => {
  const map = { P0: 'bg-red-100 text-red-600 px-2 py-1 rounded text-xs', P1: 'bg-yellow-100 text-orange-600 px-2 py-1 rounded text-xs', P2: 'bg-blue-100 text-blue-600 px-2 py-1 rounded text-xs', P3: 'bg-gray-100 text-gray-600 px-2 py-1 rounded text-xs' }
  const alias = { '紧急': 'P0', '高': 'P1', '中': 'P2', '低': 'P3' }
  const key = map[p] ? p : (alias[p] || 'P2')
  return map[key]
}

const statusText = (s) => ({ todo: '待处理', doing: '处理中', verify: '待验证', done: '已解决' }[s] || s)
const priorityText = (p) => ({ P0: '紧急', P1: '高', P2: '中', P3: '低' }[p] || p)

const filtered = computed(() => list.value.filter(i => (!filters.value.status || i.status===filters.value.status) && (!filters.value.priority || i.priority===filters.value.priority) && (!keyword.value || i.title.includes(keyword.value))))

const load = async () => {
  const params = { status: filters.value.status || undefined, priority: filters.value.priority || undefined, folder_id: (selectedFolderId.value && selectedFolderId.value !== 'trash') ? selectedFolderId.value : undefined, include_desc: selectedFolderId.value ? includeDesc.value : undefined }
  if (selectedFolderId.value === 'trash') params.status = 'trash'
  const res = await api.get('/api/issues', { params })
  list.value = res.data
}

const showAll = async () => {
  // reset all filters
  selectedFolderId.value = null
  includeDesc.value = false
  filters.value = { status: '', priority: '' }
  keyword.value = ''
  await load()
}

const remove = async (id) => {
  if (!confirm('确认删除该问题？')) return
  await api.delete(`/api/issues/${id}`)
  await load()
  try { await folderStore.load() } catch {}
}

const restore = async (id) => {
  if (!confirm('确定要还原吗？')) return
  await api.post(`/api/issues/${id}/restore`)
  await load()
  try { await folderStore.load() } catch {}
}
const hardDelete = async (id) => {
  if (!confirm('确定要彻底删除吗？此操作不可恢复！')) return
  await api.delete(`/api/issues/${id}/hard`)
  await load()
  try { await folderStore.load() } catch {}
}
const emptyTrash = async () => {
  if (!confirm('确定要清空草稿箱吗？所有项目将被永久删除！')) return
  await api.delete('/api/issues/trash/empty')
  await load()
  try { await folderStore.load() } catch {}
}

const ensureInboxFolder = async () => {
  if (!folderStore.flat.length) await folderStore.load()
  let inbox = folderStore.flat.find(f => f.name === '收集箱' && (f.parent_id == null))
  if (!inbox) {
    await folderStore.create('收集箱', null)
    await folderStore.load()
    inbox = folderStore.flat.find(f => f.name === '收集箱' && (f.parent_id == null))
  }
  return inbox?.id || null
}

const openNew = async () => {
  const inboxId = await ensureInboxFolder()
  formFolderId.value = selectedFolderId.value ?? inboxId
  show.value = true
}

const submit = async () => {
  if (!form.value.title) return
  const payload = { ...form.value, deadline: form.value.deadline || null, folder_id: formFolderId.value }
  await api.post('/api/issues', payload)
  show.value = false
  form.value = { title: '', description: '', status: 'todo', priority: 'P2', deadline: '' }
  formFolderId.value = null
  await load()
  try { await folderStore.load() } catch {}
}

const onFolderSelect = async (id) => {
  // reset all filters when switching folder
  selectedFolderId.value = id
  includeDesc.value = false
  filters.value = { status: '', priority: '' }
  keyword.value = ''
  await load()
}
const onFolderDrop = async ({ folderId, issueId }) => {
  if (!issueId) return
  await api.put(`/api/issues/${issueId}`, { folder_id: folderId })
  await load()
  try { await folderStore.load() } catch {}
}

const onDragStart = (issue, e) => {
  draggingIssueId.value = issue.id
  try { e.dataTransfer.setData('text/issue-id', String(issue.id)) } catch {}
}

const onRowDrop = (targetIssue) => {
  const srcId = draggingIssueId.value
  if (!srcId || srcId === targetIssue.id) return
  const idxSrc = list.value.findIndex(x => x.id === srcId)
  const idxTgt = list.value.findIndex(x => x.id === targetIssue.id)
  if (idxSrc < 0 || idxTgt < 0) return
  const [moved] = list.value.splice(idxSrc, 1)
  list.value.splice(idxTgt, 0, moved)
  draggingIssueId.value = null
}

const showMove = ref(false)
const moveIssueId = ref(null)
const moveFolderId = ref(null)
const openMove = async (issueId) => {
  await folderStore.load()
  moveIssueId.value = issueId
  moveFolderId.value = selectedFolderId.value ?? null
  showMove.value = true
}
const confirmMove = async () => {
  if (!moveIssueId.value && moveIssueId.value !== 0) return
  await api.put(`/api/issues/${moveIssueId.value}`, { folder_id: moveFolderId.value })
  showMove.value = false
  moveIssueId.value = null
  await load()
  try { await folderStore.load() } catch {}
}

const folderOptions = computed(() => {
  const out = []
  const walk = (nodes, depth) => {
    for (const n of nodes) {
      out.push({ id: n.id, label: `${'\u00A0'.repeat(depth*2)}${n.name}` })
      if (n.children && n.children.length) walk(n.children, depth+1)
    }
  }
  walk(folderStore.tree || [], 0)
  return out
})

onMounted(async () => { await folderStore.load(); await load() })

// dialog lifecycle: lock scroll and cleanup editor to avoid lingering popups
watch(show, (v) => {
  try { document.body.style.overflow = v ? 'hidden' : '' } catch {}
  if (!v) { editorReady.value = false }
})
onBeforeUnmount(() => {})

// Drawer: issue detail (2/3 width with animation)
const showDetail = ref(false)
const detailId = ref(null)
const detailRef = ref(null)
const openDetail = (issue) => { detailId.value = issue.id; showDetail.value = true }
const closeDetail = () => {
  try {
    const dirty = detailRef.value?.isSummaryDirty?.()
    if (dirty && !confirm('问题总结未保存，关闭后可能丢失，是否继续？')) return
  } catch {}
  showDetail.value = false; detailId.value = null
}

// utils
const stripHtml = (html) => {
  try {
    const div = document.createElement('div')
    div.innerHTML = html || ''
    const text = div.textContent || div.innerText || ''
    return text.replace(/\u00A0/g, ' ').replace(/\s+/g, ' ').trim()
  } catch { return html || '' }
}

// Helpers removed: use shared RichTextEditor component

</script>

<style>
@import "https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css";
.fade-enter-active, .fade-leave-active { transition: opacity .2s ease; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
</style>
